package com.jprocms.module.cms.es.config;

import com.jpro.framework.common.util.json.databind.NumberSerializer;
import com.jpro.framework.jackson.core.databind.OffsetDateTimeDeserializer;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author jprocms
 */
@Configuration
public class ElasticsearchConfig {

    /**
     * 创建bean为了给 springdata repo使用
     *
     * @return
     */
    @Bean
    public String contentIndex() {
        return contentIndex;
    }


    @Bean
    public ElasticsearchClient elasticsearchClient() {
        List<HttpHost> httpHostList = elasticsearchProperties.getUris().stream().map(item -> {
                    try {
                        URL url = new URL(item);
                        return new HttpHost(url.getHost(), url.getPort());
                    } catch (MalformedURLException e) {
                        return null;
                    }
                }
        ).filter(Objects::nonNull).collect(Collectors.toList());
        CredentialsProvider credentialsProvider = null;
        if (StringUtils.isNotBlank(elasticsearchProperties.getUsername()) && StringUtils.isNotBlank(elasticsearchProperties.getPassword())) {
            credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(elasticsearchProperties.getUsername(), elasticsearchProperties.getPassword()));
        }
        RestClientBuilder builder = RestClient.builder(httpHostList.toArray(new HttpHost[httpHostList.size()]));
        if (credentialsProvider != null) {
            CredentialsProvider credentialsProviderTmp = credentialsProvider;
            builder.setHttpClientConfigCallback(httpAsyncClientBuilder -> httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProviderTmp));
        }

        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        /*
         * 1. 新增Long类型序列化规则，数值超过2^53-1，在JS会出现精度丢失问题，因此Long自动序列化为字符串类型
         * 2. 新增LocalDateTime序列化、反序列化规则
         */
        simpleModule
                .addSerializer(Long.class, NumberSerializer.INSTANCE)
                .addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
                .addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
                .addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE)
                .addDeserializer(OffsetDateTime.class, OffsetDateTimeDeserializer.INSTANCE)
                .addSerializer(OffsetDateTime.class, com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer.INSTANCE);

        objectMapper.registerModules(simpleModule);

        RestClient restClient = builder.build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper));
        return new ElasticsearchClient(transport);
    }

    @Resource
    private ElasticsearchProperties elasticsearchProperties;
    @Value("${elasticsearch.contentIndex}")
    private String contentIndex;

}
